Hallitse TypeScriptin .d.ts-tiedostot: varmista tyyppiturvallisuus ja automaattinen täydennys JS-kirjastoissa. Opi @types, luo omia määrittelyjä ja käsittele ulkoista koodia.
JavaScript-ekosysteemin avaaminen: Syväsukellus TypeScriptin määrittelytiedostoihin
TypeScript on mullistanut modernin verkkokehityksen tuomalla staattisen tyypityksen JavaScriptin dynaamiseen maailmaan. Tämä tyyppiturvallisuus tarjoaa uskomattomia etuja: virheiden havaitsemisen käännösaikana, tehokkaan editorin automaattisen täydennyksen mahdollistamisen ja suurten koodikantojen ylläpidettävyyden parantamisen merkittävästi. Suuri haaste syntyy kuitenkin, kun haluamme käyttää olemassa olevien JavaScript-kirjastojen laajaa ekosysteemiä – joista useimpia ei ole kirjoitettu TypeScriptillä. Miten tiukasti tyypitetty TypeScript-koodimme ymmärtää tyypittömän JavaScript-kirjaston muodot, funktiot ja muuttujat?
Vastaus löytyy TypeScriptin määrittelytiedostoista. Nämä tiedostot, jotka tunnistaa niiden .d.ts-päätteestä, ovat olennainen silta TypeScriptin ja JavaScriptin maailmojen välillä. Ne toimivat suunnitelmana tai API-sopimuksena, kuvaten kolmannen osapuolen kirjaston tyypit ilman, että ne sisältävät sen varsinaista toteutusta. Tässä kattavassa oppaassa tutkimme kaiken, mitä sinun tarvitsee tietää hallitaksesi tyyppimäärittelyjä varmasti missä tahansa JavaScript-kirjastossa TypeScript-projekteissasi.
Mitä TypeScriptin määrittelytiedostot tarkalleen ottaen ovat?
Kuvittele, että olet palkannut urakoitsijan, joka puhuu vain eri kieltä. Jotta voisit työskennellä heidän kanssaan tehokkaasti, tarvitsisit kääntäjän tai yksityiskohtaisen ohjeistuksen kielellä, jonka te molemmat ymmärrätte. Määrittelytiedosto palvelee juuri tätä tarkoitusta TypeScript-kääntäjälle (urakoitsijalle).
.d.ts-tiedosto sisältää vain tyyppitietoja. Se sisältää:
- Funktioiden ja metodien signaturet (parametrien tyypit, palautustyypit).
- Muuttujien ja niiden tyyppien määrittelyt.
- Rajapinnat ja tyyppialiaset monimutkaisille objekteille.
- Luokkamäärittelyt, mukaan lukien niiden ominaisuudet ja metodit.
- Nimiavaruus- ja moduulirakenteet.
Oleellista on, että nämä tiedostot eivät sisällä suoritettavaa koodia. Ne ovat puhtaasti staattista analyysiä varten. Kun tuot JavaScript-kirjaston, kuten Lodashin, TypeScript-projektiisi, kääntäjä etsii vastaavan määrittelytiedoston. Jos se löytää sellaisen, se voi validoida koodisi, tarjota älykästä automaattista täydennystä ja varmistaa, että käytät kirjastoa oikein. Jos se ei löydä, se antaa virheen, kuten: Could not find a declaration file for module 'lodash'.
Miksi määrittelytiedostot ovat ehdottomia ammattimaisessa kehityksessä
JavaScript-kirjastojen käyttäminen ilman asianmukaisia tyyppimäärittelyjä TypeScript-projektissa heikentää itse syytä käyttää TypeScriptiä. Tarkastellaan yksinkertaista skenaariota käyttämällä suosittua apukirjastoa, Lodashia.
Maailma ilman tyyppimäärittelyjä
Ilman määrittelytiedostoa TypeScriptillä ei ole aavistustakaan, mikä lodash on tai mitä se sisältää. Jotta koodi edes kääntyisi, saatat olla houkutus käyttää tällaista pikakorjausta:
const _: any = require('lodash');\n\nconst users = [{ 'user': 'barney' }, { 'user': 'fred' }];\n\n// Automaattinen täydennys? Ei apua täällä.\n// Tyyppitarkistus? Ei. Onko 'username' oikea ominaisuus?\n// Kääntäjä sallii tämän, mutta se saattaa epäonnistua ajonaikana.\n_.find(users, { username: 'fred' });
Tässä tapauksessa _-muuttuja on tyyppiä any. Tämä kertoo TypeScriptille tehokkaasti: "Älä tarkista mitään tähän muuttujaan liittyvää." Menetät kaikki edut: ei automaattista täydennystä, ei tyyppitarkistusta argumenteille eikä varmuutta palautustyypistä. Tämä on otollinen maaperä ajonaikaisille virheille.
Maailma tyyppimäärittelyjen kanssa
Katsotaanpa nyt, mitä tapahtuu, kun tarjoamme tarvittavan määrittelytiedoston. Kun tyypit on asennettu (käsittelemme tätä seuraavaksi), käyttökokemus muuttuu:
import _ from 'lodash';\n\ninterface User { \n user: string;\n active?: boolean;\n}\n\nconst users: User[] = [{ 'user': 'barney' }, { 'user': 'fred' }];\n\n// 1. Editor tarjoaa automaattisen täydennyksen 'find'- ja muihin lodash-funktioihin.\n// 2. Viemällä hiiren 'find'-kohdan päälle näkyy sen koko signature ja dokumentaatio.\n// 3. TypeScript näkee, että `users` on `User`-objektien taulukko.\n// 4. TypeScript tietää, että `find`-funktion predikaatin `User[]`-joukossa tulisi sisältää `user` tai `active`.\n\n// OIKEIN: TypeScript on tyytyväinen.\nconst fred = _.find(users, { user: 'fred' });\n\n// VIRHE: TypeScript nappaa virheen!\n// Ominaisuutta 'username' ei ole tyypissä 'User'.\nconst betty = _.find(users, { username: 'betty' });
Ero on kuin yöllä ja päivällä. Saamme täyden tyyppiturvallisuuden, ylivertaisen kehittäjäkokemuksen työkalujen kautta ja dramaattisen vähennyksen mahdollisissa virheissä. Tämä on ammattimainen standardi työskenneltäessä TypeScriptin kanssa.
Tyyppimäärittelyjen löytämisen hierarkia
Miten siis saat nämä maagiset .d.ts-tiedostot suosikkikirjastoillesi? On olemassa vakiintunut prosessi, joka kattaa suurimman osan skenaarioista.
Vaihe 1: Tarkista, pakkaako kirjasto omat tyyppinsä
Paras skenaario on, kun kirjasto on kirjoitettu TypeScriptillä tai sen ylläpitäjät tarjoavat viralliset määrittelytiedostot samassa paketissa. Tämä on yleistymässä moderneissa, hyvin ylläpidetyissä projekteissa.
Miten tarkistaa:
- Asenna kirjasto tavalliseen tapaan:
npm install axios - Katso kirjaston kansioon
node_modules/axios. Näetkö siellä.d.ts-tiedostoja? - Tarkista kirjaston
package.json-tiedosto ja etsi sieltä kenttä"types"tai"typings". Tämä kenttä osoittaa suoraan päämäärittelytiedostoon. Esimerkiksi Axiosinpackage.jsonsisältää:"types": "index.d.ts".
Jos nämä ehdot täyttyvät, olet valmis! TypeScript löytää ja käyttää automaattisesti nämä mukana toimitetut tyypit. Lisätoimenpiteitä ei tarvita.
Vaihe 2: DefinitelyTyped-projekti (@types)
Niille tuhansille JavaScript-kirjastoille, jotka eivät sisällytä omia tyyppejään, maailmanlaajuinen TypeScript-yhteisö on luonut uskomattoman resurssin: DefinitelyTyped.
DefinitelyTyped on keskitetty, yhteisön ylläpitämä GitHub-arkisto, joka isännöi korkealaatuisia määrittelytiedostoja valtavalle määrälle JavaScript-paketteja. Nämä määrittelyt julkaistaan npm-rekisterissä @types-nimiavaruuden alla.
Miten sitä käytetään:
Jos kirjasto kuten lodash ei sisällytä omia tyyppejään, asennat sen vastaavan @types-paketin kehityksen riippuvuudeksi:
npm install --save-dev @types/lodash
Nimeämiskäytäntö on yksinkertainen ja ennustettavissa: paketille nimeltä package-name, sen tyypit ovat melkein aina osoitteessa @types/package-name. Voit etsiä saatavilla olevia tyyppejä npm-verkkosivustolta tai suoraan DefinitelyTyped-arkistosta.
Miksi --save-dev? Määrittelytiedostoja tarvitaan vain kehityksen ja käännöksen aikana. Ne eivät sisällä ajonaikaista koodia, joten niitä ei pitäisi sisällyttää lopulliseen tuotantopakettiisi. Niiden asentaminen devDependencyna varmistaa tämän erottelun.
Vaihe 3: Kun tyyppejä ei ole – omien kirjoittaminen
Mitä jos käytät vanhempaa, erikoisempaa tai sisäistä yksityistä kirjastoa, joka ei sisällytä tyyppejä eikä löydy DefinitelyTypedista? Tässä tapauksessa sinun on käärettävä hihat ja luotava oma määrittelytiedosto. Vaikka tämä saattaa kuulostaa pelottavalta, voit aloittaa yksinkertaisesti ja lisätä yksityiskohtia tarpeen mukaan.
Pikakorjaus: Lyhennetty ambient-moduulimäärittely
Joskus sinun tarvitsee vain saada projektisi kääntymään ilman virheitä samalla kun keksit oikean tyyppitystrategian. Voit luoda tiedoston projektiisi (esim. declarations.d.ts tai types/global.d.ts) ja lisätä lyhennetyn määrittelyn:
// .d.ts-tiedostossa\ndeclare module 'some-untyped-library';
Tämä kertoo TypeScriptille: "Luota minuun, moduuli nimeltä 'some-untyped-library' on olemassa. Käsittele kaikkea siitä tuotua tyyppiä any-tyyppinä." Tämä vaimentaa kääntäjän virheen, mutta kuten olemme keskustelleet, se uhraa kaiken tyyppiturvallisuuden kyseisen kirjaston osalta. Se on väliaikainen korjaus, ei pitkän aikavälin ratkaisu.
Perusmukautetun määrittelytiedoston luominen
Parempi lähestymistapa on alkaa määritellä tyypit niille kirjaston osille, joita todella käytät. Oletetaan, että meillä on yksinkertainen kirjasto nimeltä `string-utils`, joka vie yhden funktion.
// Tiedostossa node_modules/string-utils/index.js\nmodule.exports.capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1);\n
Voimme luoda string-utils.d.ts-tiedoston erilliseen `types`-hakemistoon projektimme juureen.
// Tiedostossa my-project/types/string-utils.d.ts\n\ndeclare module 'string-utils' {\n export function capitalize(str: string): string;\n // Voit lisätä muita funktioiden määrittelyjä tänne käyttäessäsi niitä\n // export function slugify(str: string): string;\n}
Nyt meidän on kerrottava TypeScriptille, mistä löytää mukautetut tyyppimäärittelymme. Teemme tämän tsconfig.json-tiedostossa:
{\n "compilerOptions": {\n // ... muut asetukset\n "baseUrl": ".",\n "paths": {\n "*": ["types/*"]\n }\n }\n}
Tällä asetuksella, kun tuot import { capitalize } from 'string-utils', TypeScript löytää mukautetun määrittelytiedostosi ja tarjoaa määrittelemäsi tyyppiturvallisuuden. Voit vähitellen rakentaa tätä tiedostoa käyttäessäsi lisää kirjaston ominaisuuksia.
Syvemmälle: Määrittelytiedostojen kirjoittaminen
Tutkitaan joitain edistyneempiä käsitteitä, joita kohtaat kirjoittaessasi tai lukiessasi määrittelytiedostoja.
Eri vientityyppien määrittely
JavaScript-moduulit voivat viedä asioita eri tavoin. Määrittelytiedostosi on vastattava kirjaston vientirakennetta.
- Nimetty vienti (Named Exports): Tämä on yleisin. Näimme sen yllä kohdassa `export function capitalize(...)`. Voit myös viedä vakioita, rajapintoja ja luokkia.
- Oletusvienti (Default Export): Kirjastoille, jotka käyttävät `export defaultia`.
- UMD-globaalit (UMD Globals): Vanhemmille kirjastoille, jotka on suunniteltu toimimaan selaimissa
<script>-tagin kautta, ne liittävät itsensä usein globaaliin `window`-objektiin. Voit määritellä nämä globaalit muuttujat. - `export =` ja `import = require()`: Tämä syntaksi on vanhemmille CommonJS-moduuleille, jotka käyttävät `module.exports = ...`. Esimerkiksi jos kirjasto tekee `module.exports = myClass;`.
declare module 'my-lib' {\n export const version: string;\n export interface Options { retries: number; }\n export function doSomething(options: Options): Promise<void>;\n }
declare module 'my-default-lib' {\n // Funktio-oletusvientiä varten\n export default function myCoolFunction(): void;\n\n // Objekti-oletusvientiä varten\n // const myLib = { name: 'lib', version: '1.0' };\n // export default myLib;\n }
// Määrittää globaalin muuttujan '$' tietyllä tyypillä\ndeclare var $: JQueryStatic;
// tiedostossa my-class.d.ts\ndeclare class MyClass { constructor(name: string); }\nexport = MyClass;\n\n// tiedostossa your app.ts\nimport MyClass = require('my-class');\nconst instance = new MyClass('test');
Vaikka se on harvinaisempaa modernien ES-moduulien kanssa, tämä on kriittistä yhteensopivuuden kannalta monien vanhempien, mutta silti laajasti käytettyjen Node.js-pakettien kanssa.
Moduulin laajennus: Olemassa olevien tyyppien laajentaminen
Yksi tehokkaimmista ominaisuuksista on moduulin laajennus (tunnetaan myös nimellä declaration merging). Tämä mahdollistaa ominaisuuksien lisäämisen olemassa oleviin rajapintoihin, jotka on määritelty toisen paketin määrittelytiedostossa. Tämä on erittäin hyödyllistä kirjastoille, joilla on laajennusarkkitehtuuri, kuten Express tai Fastify.
Kuvittele, että käytät Expressissä väliohjelmistoa, joka lisää `user`-ominaisuuden `Request`-objektiin. Ilman laajennusta TypeScript valittaisi, että `user`-ominaisuutta ei ole `Request`-objektissa.
Näin voit kertoa TypeScriptille tästä uudesta ominaisuudesta:
// tiedostossasi types/express.d.ts\n\n// Meidän on tuotava alkuperäinen tyyppi laajentaaksemme sitä\nimport { UserProfile } from './auth'; // Oletetaan, että sinulla on UserProfile-tyyppi\n\n// Kerro TypeScriptille, että laajennamme 'express-serve-static-core'-moduulia\ndeclare module 'express-serve-static-core' {\n // Kohdista 'Request'-rajapinta kyseisen moduulin sisällä\n interface Request {\n // Lisää mukautettu ominaisuutemme\n user?: UserProfile;\n }\n}
Nyt koko sovelluksessasi Expressin `Request`-objekti tyypitetään oikein valinnaisella `user`-ominaisuudella, ja saat täyden tyyppiturvallisuuden sekä automaattisen täydennyksen.
Kolmen vinoviivan direktiivit
Saatat joskus nähdä kommentteja .d.ts-tiedostojen alussa, jotka alkavat kolmella vinoviivalla (///). Nämä ovat kolmen vinoviivan direktiivejä, jotka toimivat kääntäjän ohjeina.
/// <reference types="..." />: Tämä on yleisin. Se sisällyttää eksplisiittisesti toisen paketin tyyppimäärittelyt riippuvuudeksi. Esimerkiksi WebdriverIO-laajennuksen tyypit saattavat sisältää/// <reference types="webdriverio" />, koska sen omat tyypit riippuvat WebdriverIO:n ydintyypeistä./// <reference path="..." />: Tätä käytetään ilmoittamaan riippuvuudesta toiseen tiedostoon samassa projektissa. Se on vanhempi syntaksi, joka on suurelta osin korvattu ES-moduulituonnilla.
Parhaat käytännöt määrittelytiedostojen hallinnassa
- Suosi mukana toimitettuja tyyppejä: Kun valitset kirjastojen välillä, suosi niitä, jotka on kirjoitettu TypeScriptillä tai jotka sisällyttävät omat viralliset tyyppimäärittelynsä. Se kertoo sitoutumisesta TypeScript-ekosysteemiin.
- Pidä
@typesdevDependencies-kohdassa: Asenna@types-paketit aina--save-devtai-D-lippujen avulla. Niitä ei tarvita tuotantokoodiisi. - Tasaa versiot: Yleinen virhelähde on kirjaston version ja sen
@types-version yhteensopimattomuus. Kirjaston suuri versionumero (esim. v2:sta v3:een) sisältää todennäköisesti API:n rikkovia muutoksia, jotka on heijastettava@types-pakettiin. Pyri pitämään ne synkronoituina. - Käytä
tsconfig.json-tiedostoa hallintaan:typeRoots- jatypes-kääntäjäasetuksettsconfig.json-tiedostossasi voivat antaa sinulle tarkan hallinnan siitä, mistä TypeScript etsii määrittelytiedostoja.typeRootskertoo kääntäjälle, mitkä kansiot tarkistaa (oletuksena se on./node_modules/@types), jatypesantaa sinun eksplisiittisesti luetella, mitkä tyyppipaketit sisällytetään. - Osallistu takaisin: Jos kirjoitat kattavan määrittelytiedoston kirjastolle, jolla ei sellaista ole, harkitse sen lisäämistä DefinitelyTyped-projektiin. Tämä on loistava tapa antaa takaisin maailmanlaajuiselle kehittäjäyhteisölle ja auttaa tuhansia muita.
Yhteenveto: Tyyppiturvallisuuden tuntemattomat sankarit
TypeScriptin määrittelytiedostot ovat tuntemattomia sankareita, jotka mahdollistavat JavaScriptin dynaamisen ja laajenevan maailman saumattoman integroinnin vankkaan, tyyppiturvalliseen kehitysympäristöön. Ne ovat kriittinen linkki, joka vahvistaa työkalujamme, ehkäisee lukemattomia virheitä ja tekee koodikannoistamme joustavampia ja itsedokumentoivampia.
Ymmärtämällä, miten löytää, käyttää ja jopa luoda omia .d.ts-tiedostoja, et vain korjaa kääntäjän virhettä – nostat koko kehitystyönkulkuasi. Avaat sekä TypeScriptin että JavaScript-kirjastojen rikkaan ekosysteemin täyden potentiaalin, luoden tehokkaan synergian, joka johtaa parempaan ja luotettavampaan ohjelmistoon globaalille yleisölle.